/*******************************************************************/
/* @ All Rights Reserved Copyright (C) 2014 broada,Co.,Ltd.        */
/*                                                                 */
/*  NAME      = frct_expect.c                                      */
/*  NOTE      = 关联连接的管理                                     */
/*  DATE      = 2015/07/27 by yaohl                                */
/*******************************************************************/

#include <stdio.h>
#include <unistd.h>
#include <sys/time.h>

#include "frcomm.h"
#include "frlog.h"
#include "frmem.h"
#include "frhash2.h"
#include "frsutl.h"

#include "conntrack.h"
#include "frame.h"

#include "frct_expect.h"

#define APM_CT_EXPECT_HASH_SIZE     128
FR_STATIC Frhash_header        st_hash_expect_ct;
FR_STATIC pthread_spinlock_t   g_spin_exp_lock;
FR_STATIC unsigned int g_ui_timeout_exp=5;

/*******************************************************************************
*  FUNC     :  释放节点内容
*  ARGS     :  p_node
*  RTN      :
*  NOTE     :
*******************************************************************************/
FR_STATIC void __frexpect_node_free(Frlist_node* p_st_node)
{
    Frexp *p_st_exp_node = NULL;

    if (p_st_node && p_st_node->data)
    {
        p_st_exp_node = (Frexp *)(p_st_node->data);
        if(p_st_exp_node->p_master_l7_module)
        {
            __sync_fetch_and_sub(&p_st_exp_node->p_master_l7_module->ul_refcnt, 1);
            p_st_exp_node->p_master_l7_module = NULL;
        }
        if(p_st_exp_node->p_lst_hdr_param)
            frexpect_free_param(&p_st_exp_node->p_lst_hdr_param);
        frfree(p_st_exp_node);
    }
    return;
}

/*******************************************************************************
*  FUNC     :  节点内容出力
*  ARGS     :  p_st_node       (Frlist_node *)
            :  p_outbuff       (出力内容指针)
            :  ui_buff_size    (出力内容空间)
*  RTN      :  比较一致返回0，其他返回1
*  NOTE     :
*******************************************************************************/
FR_STATIC void __expect_list_node_show(Frlist_node *p_st_node, char *p_outbuff, unsigned int ui_buff_size)
{
    Frexp *p_st_exp_node = NULL;
    char arr_ip_src[56], arr_ip_dst[56], arr_master_ip_src[56], arr_master_ip_dst[56];

    if(p_st_node == NULL || p_st_node->data == NULL || ui_buff_size <= 0 )
        return;

    p_st_exp_node = (Frexp *)(p_st_node->data);
    snprintf(arr_ip_src, sizeof(arr_ip_src), "%s", int_ntoa(p_st_exp_node->ui_src_ip));
    snprintf(arr_ip_dst, sizeof(arr_ip_dst), "%s", int_ntoa(p_st_exp_node->ui_dst_ip));
    snprintf(arr_master_ip_src, sizeof(arr_master_ip_src), "%s", int_ntoa(p_st_exp_node->master_src_ip));
    snprintf(arr_master_ip_dst, sizeof(arr_master_ip_dst), "%s", int_ntoa(p_st_exp_node->master_dst_ip));
    snprintf(p_outbuff, ui_buff_size, "%s:%i->%s:%i master:[%s:%i->%s:%i]"
            , arr_ip_src, p_st_exp_node->us_src_port, arr_ip_dst, p_st_exp_node->us_dst_port
           , arr_master_ip_src, p_st_exp_node->master_src_port, arr_master_ip_dst, p_st_exp_node->master_dst_port);
    return;
}

/*******************************************************************************
*  FUNC     :  关联连接初始化
*  ARGS     :
*  RTN      :  FR_SUCC :成功, FR_FAIL :失败
*  NOTE     :
*******************************************************************************/
int frexpect_init(unsigned int ui_timeout)
{
    int i_ret_fun=FR_FAIL;

    if(ui_timeout > 0)
        g_ui_timeout_exp = ui_timeout;
    i_ret_fun = frhash2_create(&st_hash_expect_ct, APM_CT_EXPECT_HASH_SIZE);
    if(i_ret_fun != FR_SUCC)
        return FR_FAIL;
    pthread_spin_init(&g_spin_exp_lock, 0);
    return FR_SUCC;
}

/*******************************************************************************
*  FUNC     :  清除超时连接
*  ARGS     :
*  RTN      :
*  NOTE     :
*******************************************************************************/
void frexpect_timeout_clean(void)
{
    int i_loop=0;
    Frlist_header *p_st_lst_hdr = NULL;
    Frlist_node *p_st_node, *p_st_node_del=NULL;
    Frexp *p_st_exp;
    struct timeval st_timeval_now;
    long l_timeout=0;

    gettimeofday (&st_timeval_now, NULL);
    pthread_spin_lock(&g_spin_exp_lock);
    FRHASH_LOOP(i_loop, &st_hash_expect_ct, p_st_lst_hdr)
    {
        p_st_node = FRLIST_FIRST_NODE(p_st_lst_hdr);
        while(p_st_node)
        {
            p_st_node_del = NULL;
            if(p_st_node && p_st_node->data)
            {
                p_st_exp = (Frexp *)(p_st_node->data);
                l_timeout = frtime_diff(&p_st_exp->st_timeval_upd, &st_timeval_now);
                if(l_timeout < 0 || l_timeout/1000000 >= g_ui_timeout_exp)
                    p_st_node_del = p_st_node;
                //FRPRINT_L4(p_st_exp->us_proto, p_st_exp->ui_src_ip, 0, p_st_exp->ui_dst_ip, p_st_exp->us_dst_port, 0, 0 );
                //frwarn("g_ui_timeout_exp=%d time =%ld\n", g_ui_timeout_exp, l_timeout/1000000);
            }
            p_st_node = FRLIST_NEXT_NODE(p_st_node);
            if(p_st_node_del)
                frhash2_del(&st_hash_expect_ct, p_st_node_del, __frexpect_node_free);
        }
    }
    pthread_spin_unlock(&g_spin_exp_lock);

    return;
}

void frexpect_setRef(Frexp_ref *p_st_expRef)
{
    if(p_st_expRef == NULL)
        return;

    p_st_expRef->p_hash_hdr = &st_hash_expect_ct;
    p_st_expRef->p_splock = &g_spin_exp_lock;
    return;
}

/*******************************************************************************
*  FUNC     :  获取关联连接
*  ARGS     :  us_proto     (协议 tcp/udp)
*           :  ui_src_ip    (原地址, 没有设置0)
*           :  us_src_port  (原端口, 没有设置0)
*           :  ui_dst_ip    (目的地址)
*           :  us_dst_port  (目的端口)
*  RTN      :  Frexp*:成功， NULL:失败
*  NOTE     :  Frexp* 需要在函数外部释放
*******************************************************************************/
Frexp *frexpect_get(u_short us_proto, u_int ui_src_ip, u_short us_src_port, u_int ui_dst_ip, u_short us_dst_port)
{
    Frlist_node *p_st_node=NULL;
    Frexp *p_st_exp=NULL;
    char arr_hash_key[30];
    int i_hash_key_len=0;

    //frwarn("expect_get %s:%i\n", int_ntoa (ui_dst_ip), us_dst_port);

    i_hash_key_len = frexpect_hash_key(us_proto, ui_src_ip, us_src_port, ui_dst_ip, us_dst_port, arr_hash_key, sizeof(arr_hash_key));
    if(i_hash_key_len <=0)
        return NULL;

    pthread_spin_lock(&g_spin_exp_lock);
    p_st_node = frhash2_getStr(&st_hash_expect_ct, arr_hash_key, i_hash_key_len);
    p_st_exp = (Frexp *)FRLIST_GET_NODE_DATA(p_st_node);
    if(p_st_exp == NULL)
    {
        if(p_st_node)
            frhash2_del(&st_hash_expect_ct, p_st_node, NULL);
        pthread_spin_unlock(&g_spin_exp_lock);
        return NULL;
    }
    if(p_st_node)
        frhash2_del(&st_hash_expect_ct, p_st_node, NULL);
    //frwarn("frexpect_get ok\n");

    pthread_spin_unlock(&g_spin_exp_lock);

    return p_st_exp;
}

/*******************************************************************************
*  FUNC     :  获取当前关联链表中个数
*  ARGS     :
*  RTN      :
*  NOTE     :
*******************************************************************************/
unsigned long frexpect_count(void)
{
    return frhash2_getCount(&st_hash_expect_ct);
}


/*******************************************************************************
*  FUNC     :  关联连接内容出力
*  ARGS     :
*  RTN      :
*  NOTE     :
*******************************************************************************/
void frexpect_output(char **pp_result)
{
    char *p_outbuff=NULL;
    int i_outbuff_size =1024000;

    p_outbuff = (char *) frmalloc(i_outbuff_size);
    if(p_outbuff==NULL)
    {
        FRLOG_ERROR_DETAIL("frmalloc err");
        return;
    }

    *p_outbuff = '\0';

    pthread_spin_lock(&g_spin_exp_lock);
    frhash2_show(&st_hash_expect_ct, p_outbuff, i_outbuff_size, __expect_list_node_show);
    pthread_spin_unlock(&g_spin_exp_lock);
    if(pp_result == NULL)
    {
        frdbg("==========================\n");
        printf("output ct expert data start\n%s\n", p_outbuff);
        frdbg("==========================\n");
        frfree(p_outbuff);
    }
    else
        *pp_result = p_outbuff;
    return;
}

/*******************************************************************************
*  FUNC     :  关联连接资源释放
*  ARGS     :
*  RTN      :
*  NOTE     :
*******************************************************************************/
void frexpect_destory(void)
{
    frhash2_free(&st_hash_expect_ct, __frexpect_node_free);
    pthread_spin_destroy(&g_spin_exp_lock);
    return;
}
